package cn.tedu.charging.cost.service.impl;

import cn.tedu.charging.common.pojo.param.ChargingProcessParam;
import cn.tedu.charging.common.pojo.vo.ChargingProcessVO;
import cn.tedu.charging.cost.dao.repository.CostRuleCacheRepository;
import cn.tedu.charging.cost.dao.repository.CostRuleRepository;
import cn.tedu.charging.cost.pojo.ChargingData;
import cn.tedu.charging.cost.pojo.po.CostRulePO;
import cn.tedu.charging.cost.service.CostService;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.ConnectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class CostServiceImpl implements CostService {
    @Autowired
    private CostRuleRepository costRepository;

    Map<String, ChargingData> chargingProcessData = new HashMap<>();

    @Override
    public ChargingProcessVO calculateCost(ChargingProcessParam chargingProcessParam) {
        ChargingData chargingData = getChargingData(chargingProcessParam);

        Integer stationId = chargingProcessParam.getStationId();
        LocalDateTime now = LocalDateTime.now();
        int hour = now.getHour();
        CostRulePO costRule = getCostRule(stationId, hour);
        if (costRule != null) {
            String name = costRule.getName();
            BigDecimal powerFee = costRule.getPowerFee();
            log.debug("当前时间(小时):{},业务时间段名称:{},电费单价:{}", hour, name, powerFee);
            chargingData.setCurrentOneFee(powerFee);

            if (chargingData.getLastChargingProcess() == null) {
                appendFirstCost2ChargingData(chargingProcessParam, chargingData, powerFee, hour, name);
            } else {
                appendLastCost2ChargingData(chargingProcessParam, chargingData, powerFee, hour, name);
            }
        }

        chargingData.setTotalCapacity(chargingProcessParam.getCapacity());
        return buildChargingProcessVO(chargingData);
    }


    private CostRulePO getCostRule(Integer stationId, int hour) {
        List<CostRulePO> costRuleList = costRulePOList(stationId);
        for (CostRulePO costRule : costRuleList) {
            if (hour >= costRule.getStartTime() && hour < costRule.getEndTime()) {
                return costRule;
            }
        }
        return null;
    }

    private ChargingData getChargingData(ChargingProcessParam chargingProcessParam) {
        ChargingData chargingData = chargingProcessData.get(chargingProcessParam.getOrderNo());
        if (chargingData == null) {
            chargingData = initChargingData(chargingProcessParam);
        }
        return chargingData;
    }

    private ChargingData initChargingData(ChargingProcessParam chargingProcessParam) {
        String orderNo = chargingProcessParam.getOrderNo();
        log.debug("通过订单号:{},没有取到充电数据,表示第一次同步充电数据", orderNo);
        ChargingData chargingData = new ChargingData();
        chargingData.setOrderNo(orderNo);
        chargingData.setUserId(chargingProcessParam.getUserId());
        chargingData.setOrderNo(orderNo);
        chargingData.setStartTime(LocalDateTime.now());
        chargingData.setGunId(chargingProcessParam.getGunId());
        chargingProcessData.put(orderNo, chargingData);
        return chargingData;
    }

    private void appendFirstCost2ChargingData(ChargingProcessParam chargingProcessParam, ChargingData chargingData, BigDecimal powerFee, int hour, String name) {
        float capacity = chargingProcessParam.getCapacity();
        BigDecimal DecimalCapacity = new BigDecimal(capacity);
        BigDecimal totalCost = getCost(DecimalCapacity, powerFee);
        chargingData.setTotalCost(totalCost);
        chargingData.setLastChargingProcess(chargingProcessParam);
    }

    private void appendLastCost2ChargingData(ChargingProcessParam chargingProcessParam, ChargingData chargingData, BigDecimal powerFee, int hour, String name) {
        BigDecimal DecimalCapacity = getRealCapacity(chargingProcessParam, chargingData);
        BigDecimal totalCost = getCost(DecimalCapacity, powerFee);
        BigDecimal lastTotalCost = chargingData.getTotalCost();
        BigDecimal currentTotalCost = lastTotalCost.add(totalCost);
        chargingData.setTotalCost(currentTotalCost);
        chargingData.setLastChargingProcess(chargingProcessParam);
        log.debug("当前时间(小时):{},计价规则名称:{},电费单价:{},充电度数:{},本次花费:{},总花费:{}",
                hour, name, powerFee, DecimalCapacity, totalCost, currentTotalCost);
    }

    private ChargingProcessVO buildChargingProcessVO(ChargingData chargingData) {
        ChargingProcessVO chargingProcessVO = new ChargingProcessVO();
        chargingProcessVO.setOrderNo(chargingData.getOrderNo());
        chargingProcessVO.setGunId(chargingData.getGunId());
        chargingProcessVO.setUserId(chargingData.getUserId());
        chargingProcessVO.setTotalCost(chargingData.getTotalCost());
        chargingProcessVO.setTotalCapacity(chargingData.getTotalCapacity());
        chargingProcessVO.setPowerFee(chargingData.getCurrentOneFee());
        Duration between = Duration.between(chargingData.getStartTime(), LocalDateTime.now());
        String chargingTime = between.toDays() + "-" + between.toHours() + "-" + between.toMinutes() + "-" + between.toMillis();
        chargingProcessVO.setTotalTime(chargingTime);
        return chargingProcessVO;
    }

    @Autowired
    private CostRuleCacheRepository costRuleCacheRepository;

    public List<CostRulePO> costRulePOList(Integer stationId) {
        List<CostRulePO> cacheCostRules = costRuleCacheRepository.getCostRulesByStationId(stationId);
        if (!CollectionUtils.isEmpty(cacheCostRules)){
            return cacheCostRules;
        }else {
            List<CostRulePO> dbCostRulePOList = costRepository.getCostRuleList(stationId);
            if (!CollectionUtils.isEmpty(dbCostRulePOList)){
                costRuleCacheRepository.saveCostRule2Redis(stationId,dbCostRulePOList);
                return dbCostRulePOList;
            }
        }
        return Collections.emptyList();
    }

    private BigDecimal getRealCapacity(ChargingProcessParam chargingProcessParam, ChargingData chargingData) {
        float lastCapacity = chargingData.getLastChargingProcess().getCapacity();
        float currentCapacity = chargingProcessParam.getCapacity();
        float realCapacity = currentCapacity - lastCapacity;
        String stringStr = Float.toString(realCapacity);
        return new BigDecimal(stringStr);
    }

    private BigDecimal getCost(BigDecimal capacity, BigDecimal powerFee) {
        return capacity.multiply(powerFee);
    }
}
